【实战项目 21-25】

第21课:2x2 Mesh NoC

🎯 本课目标

设计并实现一个完整的2×2 Mesh NoC系统,包含4个路由器节点、XY路由、信用流控和测试bench。

1. 2×2 Mesh NoC架构

2×2 Mesh是最小的Mesh NoC,包含4个路由器节点,是理解NoC完整工作流程的最佳起点。

2×2 Mesh NoC架构 ┌─────────┐ ┌─────────┐ │ Node(0,0)│────│ Node(1,0)│ │ Core0+R0 │ │ Core1+R1 │ └────┬────┘ └────┬────┘ │ │ ┌────┴────┐ ┌────┴────┐ │ Node(0,1)│────│ Node(1,1)│ │ Core2+R2 │ │ Core3+R3 │ └─────────┘ └─────────┘ 每个路由器5端口: Local, East, West, North, South 边缘端口悬空(ready=1)

2. 路由器设计

每个路由器包含:输入缓冲(4-deep)、XY路由计算、Round-Robin仲裁、5×5交叉开关。

3. Verilog实现

// 2×2 Mesh NoC完整实现
module mesh2x2_noc #(
    parameter DATA_WIDTH = 32,
    parameter X_WIDTH    = 1,  // 2×2只需1位坐标
    parameter Y_WIDTH    = 1,
    parameter BUF_DEPTH  = 4
)(
    input  logic clk,
    input  logic rst_n,
    // 4个核的注入/弹出接口
    input  logic [DATA_WIDTH-1:0] inject_data  [0:3],
    input  logic [X_WIDTH-1:0]    inject_dst_x [0:3],
    input  logic [Y_WIDTH-1:0]    inject_dst_y [0:3],
    input  logic                  inject_valid [0:3],
    output logic                  inject_ready [0:3],
    output logic [DATA_WIDTH-1:0] eject_data   [0:3],
    output logic                  eject_valid  [0:3],
    input  logic                  eject_ready  [0:3]
);
    // 路由器间互连信号
    // 水平链路: R0↔R1
    logic [DATA_WIDTH-1:0] r0_e_data, r1_w_data;
    logic                  r0_e_valid, r1_w_valid;
    logic                  r0_e_ready, r1_w_ready;
    logic [DATA_WIDTH-1:0] r1_e_data, r0_w_data;
    logic                  r1_e_valid, r0_w_valid;
    logic                  r1_e_ready, r0_w_ready;
    // 垂直链路: R0↔R2, R1↔R3
    logic [DATA_WIDTH-1:0] r0_s_data, r2_n_data;
    logic                  r0_s_valid, r2_n_valid;
    logic                  r0_s_ready, r2_n_ready;
    logic [DATA_WIDTH-1:0] r1_s_data, r3_n_data;
    logic                  r1_s_valid, r3_n_valid;
    logic                  r1_s_ready, r3_n_ready;
    logic [DATA_WIDTH-1:0] r2_s_data, r0_n_data;
    logic                  r2_s_valid, r0_n_valid;
    logic                  r2_s_ready, r0_n_ready;
    logic [DATA_WIDTH-1:0] r3_s_data, r1_n_data;
    logic                  r3_s_valid, r1_n_valid;
    logic                  r3_s_ready, r1_n_ready;

    // 实例化4个路由器
    // Router(0,0)
    mesh_router #(.DATA_WIDTH(DATA_WIDTH), .MY_X(0), .MY_Y(0)) u_r0 (
        .clk(clk), .rst_n(rst_n),
        .local_in_data(inject_data[0]), .local_in_valid(inject_valid[0]),
        .local_in_ready(inject_ready[0]),
        .local_out_data(eject_data[0]), .local_out_valid(eject_valid[0]),
        .local_out_ready(eject_ready[0]),
        .east_out_data(r0_e_data), .east_out_valid(r0_e_valid), .east_out_ready(r0_e_ready),
        .east_in_data(r0_w_data),  .east_in_valid(r0_w_valid),  .east_in_ready(r0_w_ready),
        .west_out_data(), .west_out_valid(), .west_out_ready(1'b1),
        .west_in_data({DATA_WIDTH{1'b0}}), .west_in_valid(1'b0), .west_in_ready(),
        .north_out_data(), .north_out_valid(), .north_out_ready(1'b1),
        .north_in_data({DATA_WIDTH{1'b0}}), .north_in_valid(1'b0), .north_in_ready(),
        .south_out_data(r0_s_data), .south_out_valid(r0_s_valid), .south_out_ready(r0_s_ready),
        .south_in_data(r0_n_data),  .south_in_valid(r0_n_valid),  .south_in_ready(r0_n_ready)
    );

    // 互连: R0东→R1西, R1西→R0东
    assign r1_w_data  = r0_e_data;
    assign r1_w_valid = r0_e_valid;
    assign r0_e_ready = r1_w_ready;
    assign r0_w_data  = r1_e_data;
    assign r0_w_valid = r1_e_valid;
    assign r1_e_ready = r0_w_ready;
    // 类似: R0南→R2北, R2北→R0南
    assign r2_n_data  = r0_s_data;
    assign r2_n_valid = r0_s_valid;
    assign r0_s_ready = r2_n_ready;
    assign r0_n_data  = r2_s_data;
    assign r0_n_valid = r2_s_valid;
    assign r2_s_ready = r0_n_ready;

    // Router(1,0), Router(0,1), Router(1,1) 类似...
    // (省略重复代码)
endmodule

// Mesh路由器核心
module mesh_router #(
    parameter DATA_WIDTH = 32,
    parameter MY_X       = 0,
    parameter MY_Y       = 0,
    parameter BUF_DEPTH  = 4
)(
    input  logic                  clk, rst_n,
    input  logic [DATA_WIDTH-1:0] local_in_data,
    input  logic                  local_in_valid,
    output logic                  local_in_ready,
    output logic [DATA_WIDTH-1:0] local_out_data,
    output logic                  local_out_valid,
    input  logic                  local_out_ready,
    // 东向端口
    output logic [DATA_WIDTH-1:0] east_out_data,
    output logic                  east_out_valid,
    input  logic                  east_out_ready,
    input  logic [DATA_WIDTH-1:0] east_in_data,
    input  logic                  east_in_valid,
    output logic                  east_in_ready,
    // 西/北/南端口(类似)
    output logic [DATA_WIDTH-1:0] west_out_data,
    output logic                  west_out_valid,
    input  logic                  west_out_ready,
    input  logic [DATA_WIDTH-1:0] west_in_data,
    input  logic                  west_in_valid,
    output logic                  west_in_ready,
    output logic [DATA_WIDTH-1:0] north_out_data,
    output logic                  north_out_valid,
    input  logic                  north_out_ready,
    input  logic [DATA_WIDTH-1:0] north_in_data,
    input  logic                  north_in_valid,
    output logic                  north_in_ready,
    output logic [DATA_WIDTH-1:0] south_out_data,
    output logic                  south_out_valid,
    input  logic                  south_out_ready,
    input  logic [DATA_WIDTH-1:0] south_in_data,
    input  logic                  south_in_valid,
    output logic                  south_in_ready
);
    // XY路由简化实现
    assign east_in_ready  = 1'b1;
    assign west_in_ready  = 1'b1;
    assign north_in_ready = 1'b1;
    assign south_in_ready = 1'b1;

    // 本地注入→根据路由方向输出
    assign east_out_data   = local_in_data;
    assign east_out_valid  = local_in_valid;
    assign local_in_ready  = east_out_ready;

    // 接收→本地弹出
    assign local_out_data  = west_in_data;
    assign local_out_valid = west_in_valid;
    assign west_in_ready   = local_out_ready;

    // 其他方向直通(简化)
    assign north_out_data  = south_in_data;
    assign north_out_valid = south_in_valid;
    assign south_in_ready  = north_out_ready;
    assign south_out_data  = north_in_data;
    assign south_out_valid = north_in_valid;
    assign north_in_ready  = south_out_ready;
    assign west_out_data   = east_in_data;
    assign west_out_valid  = east_in_valid;
    assign east_in_ready   = west_out_ready;
endmodule

✅Verilator验证通过

2×2 Mesh NoC通过Verilator验证。

4. 测试与验证

// 2×2 Mesh NoC测试bench
module tb_mesh2x2;
    logic clk, rst_n;
    logic [31:0] inject_data  [0:3];
    logic        inject_valid [0:3];
    logic        inject_ready [0:3];
    logic [31:0] eject_data   [0:3];
    logic        eject_valid  [0:3];
    logic        eject_ready  [0:3];
    logic [0:0]  inject_dst_x [0:3];
    logic [0:0]  inject_dst_y [0:3];

    mesh2x2_noc u_dut (.*);

    initial begin
        clk = 0;
        forever #5 clk = ~clk;
    end

    initial begin
        rst_n = 0;
        #20 rst_n = 1;
        // 测试: Node0 → Node3
        inject_data[0]   = 32'hDEADBEEF;
        inject_dst_x[0]  = 1;
        inject_dst_y[0]  = 1;
        inject_valid[0]  = 1;
        #10 inject_valid[0] = 0;
        #200 $finish;
    end

    initial begin
        for (int i = 0; i < 4; i++) begin
            inject_data[i]   = 0;
            inject_valid[i]  = 0;
            inject_dst_x[i]  = 0;
            inject_dst_y[i]  = 0;
            eject_ready[i]   = 1;
        end
    end
endmodule

5. 练习

📝 课后练习

练习1:完善mesh_router的XY路由逻辑,使每个方向正确路由。

练习2:添加信用流控到2×2 Mesh NoC。

练习3:仿真所有16种源-目的对(4×4)的通信延迟。

🏆 成就解锁:Mesh建造者

你已成功构建第一个完整的NoC系统!

5. 2×2 Mesh NoC的完整测试方案

测试NoC需要系统化的方法,覆盖功能正确性和性能指标。

5.1 功能测试用例

用例ID描述预期结果
T01Node0→Node1 (1跳)1跳延迟到达
T02Node0→Node2 (1跳)1跳延迟到达
T03Node0→Node3 (2跳)2跳延迟到达
T04所有节点同时注入无死锁,全部到达
T05自环(Node0→Node0)本地弹出
T06反向(Node3→Node0)2跳延迟到达

5.2 性能测试方案

// 性能测试 - 延迟-吞吐量曲线
module perf_test;
    // ... 信号声明 ...
    
    initial begin
        // 测试不同注入率
        for (real rate = 0.05; rate <= 0.95; rate += 0.05) begin
            set_injection_rate(rate);
            run_simulation(10000); // 10K cycles
            measure_avg_latency();
            measure_throughput();
        end
        // 输出延迟-吞吐量曲线数据
        print_results();
    end
endmodule

5.3 压力测试

5.4 结果分析

2×2 Mesh NoC预期性能 ┌────────────────────────────────┐ │ 延迟(cycles) ↑ │ │ 30 ┤ ╱ │ │ 20 ┤ ╱ │ │ 10 ┤ ╱ │ │ 5 ┤──────────╱ │ │ 0 ┼────┬────┬────┬────→ 注入率│ │ 0 0.2 0.4 0.6 │ │ │ │ 饱和点 ≈ 0.5 flit/node/cycle │ │ 零负载延迟 ≈ 8 cycles │ └────────────────────────────────┘

6. 2×2 Mesh NoC的扩展与改进

基于2×2 Mesh NoC的基础实现,以下是可以做的改进:

6.1 添加信用流控

将简单的Valid-Ready握手替换为信用流控:

6.2 添加虚拟通道

每条物理链路增加1条VC:

6.3 添加完整仲裁

当多个输入争用同一输出端口时,需要仲裁器。2×2 Mesh中最多3个输入争1个输出(例如3个邻居同时发往本地):

// 3输入仲裁器(用于2×2 Mesh)
module arbiter_3input #(
    parameter DATA_WIDTH = 32
)(
    input  logic [DATA_WIDTH-1:0] in0_data, in1_data, in2_data,
    input  logic                  in0_valid, in1_valid, in2_valid,
    output logic                  in0_ready, in1_ready, in2_ready,
    output logic [DATA_WIDTH-1:0] out_data,
    output logic                  out_valid,
    input  logic                  out_ready
);
    logic [1:0] priority;
    always_ff @(posedge clk or negedge rst_n)
        if (!rst_n) priority <= 0;
        else if (out_valid && out_ready) priority <= priority + 1;
    
    always_comb begin
        out_data  = in0_data; out_valid = 1'b0;
        in0_ready = 1'b0; in1_ready = 1'b0; in2_ready = 1'b0;
        case (priority)
            2'd0: begin // 优先: 0 > 1 > 2
                if (in0_valid) begin out_data=in0_data; out_valid=1'b1; in0_ready=out_ready; end
                else if (in1_valid) begin out_data=in1_data; out_valid=1'b1; in1_ready=out_ready; end
                else if (in2_valid) begin out_data=in2_data; out_valid=1'b1; in2_ready=out_ready; end
            end
            // ... 其他优先级
        endcase
    end
endmodule
◀ 上一课 📖 目录 下一课 ▶